package org.easystub;

import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Method;

public final class EasyStub {

    private EasyStub() {
    }

    private static final Map<Object, InvocationReporter> stubToReporter = new HashMap<Object, InvocationReporter>();
    private static final Map<Object, RecordedInvocationAnswerer> stubToAnswerer = new HashMap<Object, RecordedInvocationAnswerer>();
    private static final Map<Object, CaptureStore> stubToCaptureStore = new HashMap<Object, CaptureStore>();
    private static final ProxyFactory PROXY_FACTORY = new ProxyFactory();

    public static <T> T createStub(Class<T> toStub) {
        return createStub(toStub, DefaultReply.DEFAULT_VALUES);
    }

    public static <T> T createStub(Class<T> toStub, DefaultReply defaultReplyBehavior) {
        InvocationHandlerParameters<T> params = new InvocationHandlerParameters<T>(toStub, defaultReplyBehavior);
        T stub = createProxyInstance(params);
        stubToAnswerer.put(stub, params.getRecordedAnswerer());
        stubToReporter.put(stub, params.getReporter());
        stubToCaptureStore.put(stub, params.getCaptureStore());
        return stub;
    }

    public static <T> ResultReporter<T> stub(T invocation) {
        return createResultReporterForLastInvocation();
    }

    public static ResultReporter<Void> replyToLastMethodCall() {
        return createResultReporterForLastInvocation();
    }

    private static <T> ResultReporter<T> createResultReporterForLastInvocation() {
        Invocation last = GlobalInvocationReporter.lastInvocation();
        RecordedInvocationAnswerer answerer = stubToAnswerer.get(last.getObjectCalled());
        InvocationReporter reporter = stubToReporter.get(last.getObjectCalled());
        return new ResultReporter<T>(reporter, answerer);
    }

    public static <T> T any(Class<T> argumentClass) {
        return null;
    }

    @SuppressWarnings("unchecked")
    private static <T> T createProxyInstance(InvocationHandlerParameters<T> params) {
        return PROXY_FACTORY.createProxyInstance(params);
    }


    public static CaptureReporter setupCapture(Object invocation) {
        return setupCaptureForLastInvocation();
    }

    public static CaptureReporter setupCaptureForLastInvocation() {
        Invocation last = GlobalInvocationReporter.lastInvocation();
        guardAgainstNoArgMethod(last.getMethodCalled());
        
        InvocationReporter reporter = stubToReporter.get(last.getObjectCalled());
        CaptureStore store = stubToCaptureStore.get(last.getObjectCalled());

        return new CaptureReporter(reporter, store);
    }

    private static void guardAgainstNoArgMethod(Method methodCalled) {
        if(methodCalled.getParameterTypes().length == 0) {
            throw new UnsupportedOperationException("Cannot register capturing on methods without arguments");
        }
    }


}
